// stdafx.cpp : source file that includes just the standard includes
// XortDebugEngine.pch will be the pre-compiled header
// stdafx.obj will contain the pre-compiled type information

#include "stdafx.h"

// This was taken directly from CRT :)

#define NULCHAR    '\0'
#define SPACECHAR  ' '
#define TABCHAR    '\t'
#define DQUOTECHAR '\"'
#define SLASHCHAR  '\\'

/***
*static void parse_cmdline(cmdstart, argv, args, numargs, numchars)
*
*Purpose:
*       Parses the command line and sets up the argv[] array.
*       On entry, cmdstart should point to the command line,
*       argv should point to memory for the argv array, args
*       points to memory to place the text of the arguments.
*       If these are NULL, then no storing (only counting)
*       is done.  On exit, *numargs has the number of
*       arguments (plus one for a final NULL argument),
*       and *numchars has the number of bytes used in the buffer
*       pointed to by args.
*
*Entry:
*       CHAR *cmdstart - pointer to command line of the form
*           <progname><nul><args><nul>
*       CHAR **argv - where to build argv array; NULL means don't
*                       build array
*       CHAR *args - where to place argument text; NULL means don't
*                       store text
*
*Exit:
*       no return value
*       int *numargs - returns number of argv entries created
*       int *numchars - number of characters used in args buffer
*
*Exceptions:
*
*******************************************************************************/

static void parse_cmdline(
    CHAR *cmdstart,
    CHAR **argv,
    CHAR *args,
    int *numargs,
    int *numchars
    )
{
        CHAR *p;
        BYTE c;
        int inquote;                    /* 1 = inside quotes */
        int copychar;                   /* 1 = copy char to *args */
        unsigned numslash;              /* num of backslashes seen */

        *numchars = 0;
        *numargs = 1;                   /* the program name at least */

        /* first scan the program name, copy it, and count the bytes */
        p = cmdstart;
        if (argv)
            *argv++ = args;

        /* A quoted program name is handled here. The handling is much
           simpler than for other arguments. Basically, whatever lies
           between the leading double-quote and next one, or a terminal null
           character is simply accepted. Fancier handling is not required
           because the program name must be a legal NTFS/HPFS file name.
           Note that the double-quote characters are not copied, nor do they
           contribute to numchars. */
        inquote = FALSE;
        do {
            if (*p == DQUOTECHAR )
            {
                inquote = !inquote;
                c = (BYTE) *p++;
                continue;
            }
            ++*numchars;
            if (args)
                *args++ = *p;

            c = (BYTE) *p++;

        } while ( (c != NULCHAR && (inquote || (c !=SPACECHAR && c != TABCHAR))) );

        if ( c == NULCHAR ) {
            p--;
        } else {
            if (args)
                *(args-1) = NULCHAR;
        }

        inquote = 0;

        /* loop on each argument */
        for(;;)
        {
            if ( *p ) {
                while (*p == SPACECHAR || *p == TABCHAR)
                    ++p;
            }

            if (*p == NULCHAR)
                break;              /* end of args */

            /* scan an argument */
            if (argv)
                *argv++ = args;     /* store ptr to arg */
            ++*numargs;

            /* loop through scanning one argument */
            for (;;)
            {
                copychar = 1;
                /* Rules: 2N backslashes + " ==> N backslashes and begin/end quote
                   2N+1 backslashes + " ==> N backslashes + literal "
                   N backslashes ==> N backslashes */
                numslash = 0;
                while (*p == SLASHCHAR) {
                    /* count number of backslashes for use below */
                    ++p;
                    ++numslash;
                }
                if (*p == DQUOTECHAR) {
                    /* if 2N backslashes before, start/end quote, otherwise
                        copy literally */
                    if (numslash % 2 == 0) {
                        if (inquote && p[1] == DQUOTECHAR) {
                            p++;    /* Double quote inside quoted string */
                        } else {    /* skip first quote char and copy second */
                            copychar = 0;       /* don't copy quote */
                            inquote = !inquote;
                        }
                    }
                    numslash /= 2;          /* divide numslash by two */
                }

                /* copy slashes */
                while (numslash--) {
                    if (args)
                        *args++ = SLASHCHAR;
                    ++*numchars;
                }

                /* if at end of arg, break loop */
                if (*p == NULCHAR || (!inquote && (*p == SPACECHAR || *p == TABCHAR)))
                    break;

                /* copy character into argument */
                if (copychar) {
                    if (args)
                        *args++ = *p;
                    ++*numchars;
                }

                ++p;
            }

            /* null-terminate the argument */

            if (args)
                *args++ = NULCHAR;          /* terminate string */
            ++*numchars;
        }

        /* We put one last argument in -- a null ptr */
        if (argv)
            *argv++ = NULL;
        ++*numargs;
}

/***
*_setargv, __setargv - set up "argc" and "argv" for C programs
*
*Purpose:
*       Read the command line and create the argv array for C
*       programs.
*
*Entry:
*       Arguments are retrieved from the program command line,
*       pointed to by _acmdln.
*
*Exit:
*       Returns 0 if successful, -1 if memory allocation failed.
*       "argv" points to a null-terminated list of pointers to ASCIZ
*       strings, each of which is an argument from the command line.
*       "argc" is the number of arguments.  The strings are copied from
*       the environment segment into space allocated on the heap/stack.
*       The list of pointers is also located on the heap or stack.
*       _pgmptr points to the program name.
*
*Exceptions:
*       Terminates with out of memory error if no memory to allocate.
*
*******************************************************************************/
int ParseCmdLine( CHAR *cmdstart /* start of command line to parse */, int *piArgc, char ***ppArgv )
{
        CHAR *p = NULL;
        int numargs, numchars;

        static CHAR _pgmname[ MAX_PATH + 1 ];

        parse_cmdline(cmdstart, NULL, NULL, &numargs, &numchars);

        /* allocate space for argv[] vector and strings */
        if (numargs >= (SIZE_MAX / sizeof(CHAR *)) ||
            numchars >= (SIZE_MAX / sizeof(CHAR)))
        {
            return -1;
        }
        if ((numargs * sizeof(CHAR *) + numchars * sizeof(CHAR)) < (numchars * sizeof(CHAR)))
        {
            return -1;
        }
        p = (char*)malloc( numargs * sizeof(CHAR *) + numchars * sizeof(CHAR) );
        if( p == NULL )
            return -1;

        /* store args and argv ptrs in just allocated block */

        parse_cmdline(cmdstart, (char **)p, p + numargs * sizeof(char *), &numargs, &numchars);

        /* set argv and argc */
        *piArgc = numargs - 1;
        *ppArgv = (char **)p;

        return 0;
}
